package com.zgjl.api.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.KeyParameter;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;

/**
 * HmacSM3Util
 *
 * 国密 HMAC-SM3 签名工具类
 * 功能：
 * 1. 对 JSON 数据进行 canonicalization（key 升序 + 去空格）
 * 2. 使用 HMAC-SM3 签名
 * 3. 输出 Base64 编码签名
 * 4. 支持验签
 */
public class HmacSM3Util {

    private static final ObjectMapper mapper = new ObjectMapper();

    private static long timeWindowMs;

    // 设置静态密钥
    public static void setTimeWindowMs(long timeWindowMs) {
        HmacSM3Util.timeWindowMs = timeWindowMs;
    }


    /**
     * 将 JSON 转成 TreeMap 按 key 升序，再输出标准化 JSON
     * 解决 HMAC 对字段顺序敏感的问题
     * @param json 原始 JSON 字符串
     * @return canonical JSON（key 升序 + 无多余空格）
     * @throws Exception
     */
    private static String canonicalizeJson(String json) throws Exception {
        Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
        Map<String, Object> sortedMap = new TreeMap<>(map);
        return mapper.writeValueAsString(sortedMap);
    }

    /**
     * 对 JSON 数据进行 HMAC-SM3 签名
     * @param jsonData JSON 字符串
     * @param secretKey Base64 编码密钥
     * @return Base64 编码签名
     */
    public static String sign(String jsonData, String secretKey) throws Exception {
        String canonicalJson = canonicalizeJson(jsonData);
        byte[] keyBytes = Base64.getDecoder().decode(secretKey);
        KeyParameter keyParam = new KeyParameter(keyBytes);
        // 初始化 HMAC-SM3
        Mac hmac = new HMac(new SM3Digest());
        hmac.init(keyParam);
        // 签名数据
        byte[] input = canonicalJson.getBytes(StandardCharsets.UTF_8);
        hmac.update(input, 0, input.length);
        byte[] output = new byte[hmac.getMacSize()];
        hmac.doFinal(output, 0);
        // Base64 输出
        return Base64.getEncoder().encodeToString(output);
    }

    /**
     * 验签
     * @param jsonData 原始 JSON
     * @param signature Base64 编码签名
     * @param secretKey Base64 编码密钥
     * @return true 验签通过
     */
    public static boolean verify(String jsonData, String signature, String secretKey) throws Exception {
        String calcSign = sign(jsonData, secretKey);
        return calcSign.equals(signature);
    }

    /**
     * 检查请求时间戳是否在允许窗口内（毫秒级）
     *
     * @param timestampMillis 客户端传过来的 timestamp（毫秒）
     * @return true 表示有效，false 表示超时
     */
    public static boolean isTimestampValid(long timestampMillis) {
        long now = System.currentTimeMillis();
        long diff = Math.abs(now - timestampMillis);
        return diff <= timeWindowMs;
    }
}
